package com.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.common.constants.Constant;
import com.common.exception.BusinessException;
import com.common.exception.code.BaseResponseCode;
import com.common.service.RedisService;
import com.common.utils.PageUtil;
import com.common.utils.TokenSettings;
import com.system.entity.Role;
import com.system.entity.RolePermission;
import com.system.mapper.RoleMapper;
import com.system.mapper.UserRoleMapper;
import com.system.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.system.vo.request.AddRoleReqVO;
import com.system.vo.request.RolePageReqVO;
import com.system.vo.request.RoleUpdateReqVO;
import com.system.vo.response.PageVO;
import com.system.vo.response.PermissionRespNodeVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author XueHeng
 * @since 2020-09-17
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private RolePermissionService rolePermissionService;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private TokenSettings tokenSettings;
    @Autowired
    private PermissionService permissionService;

    @Override
    public List<Role> getRoleByUserId(String userId) {
        return this.baseMapper.getRoleByUserId(userId);
    }

    @Override
    public List<String> getRoleNames(String userId) {
        List<String> roleNames = new ArrayList<>();
        List<Role> sysRoles = getRoleByUserId(userId);
        sysRoles.stream().forEach(role->{
            roleNames.add(role.getName());
        });
        return roleNames;
    }

    @Override
    public PageVO<Role> getRoles(RolePageReqVO vo) {
        Page<Role> page = new Page<>(vo.getPageNum(),vo.getPageSize());
        IPage<Role> roles = this.baseMapper.getRoles(page,vo);
        return PageUtil.getPage(roles);
    }

    @Override
    @Transactional
    public void add(AddRoleReqVO vo) {
        Role role = new Role();
        String roleId = UUID.randomUUID().toString();
        role.setCreateTime(new Date());
        role.setId(roleId);
        BeanUtils.copyProperties(vo,role);
        this.save(role);
        // 关联角色权限关系
        List<String> permissionIds = vo.getPermissions();
        if(!permissionIds.isEmpty()) {
            List<RolePermission> rolePermissions = new ArrayList<>();
            for (String permissionId : permissionIds) {
                RolePermission rolePermission = new RolePermission();
                rolePermission.setId(UUID.randomUUID().toString());
                rolePermission.setCreateTime(new Date());
                rolePermission.setRoleId(roleId);
                rolePermission.setPermissionId(permissionId);
                rolePermissions.add(rolePermission);
            }
            this.rolePermissionService.saveBatch(rolePermissions);
        }
    }

    @Override
    @Transactional
    public void delete(String id) {
        this.removeById(id);
    }

    @Override
    public Role getRole(String roleId) {
        Role role = this.baseMapper.selectById(roleId);
        if(role==null)
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        //获取权限树-按钮
        List<PermissionRespNodeVO> permissionList = permissionService.treeAll();
        //获取该角色拥有的菜单权限
        Set<String> permissionIds = rolePermissionService.getpermissionIds(roleId).stream().collect(Collectors.toSet());
        this.setChecked(permissionList,permissionIds);
        role.setPermissionRespNode(permissionList);
        return role;
    }

    /*
     拥有的菜单标记为选中
     子集选中从它往上到跟目录都被选中，父级选中从它到它所有的叶子节点都会被选中
     */
    private void setChecked(List<PermissionRespNodeVO> permissionList,Set<String> permissionIds){
        for(PermissionRespNodeVO node:permissionList) {
            if (permissionIds.contains(node.getId()) && (node.getChildren() == null || node.getChildren().isEmpty()))
                node.setChecked(true);
            setChecked((List<PermissionRespNodeVO>) node.getChildren(),permissionIds);
        }
    }

    @Override
    @Transactional
    public void update(RoleUpdateReqVO vo) {
        Role role = this.baseMapper.selectById(vo.getId());
        if(role==null)
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        BeanUtils.copyProperties(vo,role);
        role.setUpdateTime(new Date());
        this.updateById(role);
        // 关联角色和权限关系
        // 先删除角色和权限关系
        this.rolePermissionService.remove(new QueryWrapper<RolePermission>().eq("role_id",role.getId()));
        // 后新增角色和权限关系
        List<String> permissionIds = vo.getPermissions();
        if(!permissionIds.isEmpty()) {
            List<RolePermission> rolePermissions = new ArrayList<>();
            for (String permissionId : permissionIds) {
                RolePermission rolePermission = new RolePermission();
                rolePermission.setId(UUID.randomUUID().toString());
                rolePermission.setRoleId(role.getId());
                rolePermission.setCreateTime(new Date());
                rolePermission.setPermissionId(permissionId);
                rolePermissions.add(rolePermission);
            }
            this.rolePermissionService.saveBatch(rolePermissions);
        }
        // 通知拥有该角色的用户更新业务token
        List<String> userIds = this.userRoleMapper.getUserIds(vo.getId());
        for(String userId:userIds){
            // 通知拥有该角色的用户更新业务token
            redisService.set(Constant.JWT_REFRESH_KEY+userId,userId,tokenSettings.getAccessTokenExpireTime().toMillis(), TimeUnit.MILLISECONDS);
            // 清楚用户授权数据缓存
            redisService.delete(Constant.IDENTIFY_CACHE_KEY+userId);
        }
    }
}
